#include "lan911x_native.cc"
#include "astralapi.h"


/* ===============================================================================================================
 *                              Here we start the functions to operate RX/TX/Init
 * ==============================================================================================================*/

static int transmit(void* base, const char* buf, const int byte_cnt)
{
    // At the heart of the transmit packet process, two 32-bit values called TX_CMD_A and TX_CMD_B
    // command words are written into the device transmit data register (TX_DATA_FIFO), followed by the
    // packet buffer data.
    // A+B+payload + {A+palyload} x N

    LOCAL_U32_FEILD(vTX_CMD_A)
    LOCAL_U32_FEILD(vTX_CMD_B)
    LOCAL_U32_FEILD(vTX_FIFO_INF)
    LOCAL_U32_FEILD(vTX_STATUS);

    const uint32 *data = (const uint32*)buf;
    const int length = byte_cnt;

    vTX_CMD_A.feild.int_on_done = 1;
    vTX_CMD_A.feild.first_seg = 1;
    vTX_CMD_A.feild.last_seg = 1;
    vTX_CMD_A.feild.buff_size = length;
    vTX_CMD_A.feild.end_align = ALIGN_4B;

    vTX_CMD_B.feild.pack_len = length;
    vTX_CMD_B.feild.crc_dis = 0;

    write_mmaped_reg(base, TX_DATA_FIFO, vTX_CMD_A.val);
    write_mmaped_reg(base, TX_DATA_FIFO, vTX_CMD_B.val);

    asm volatile("cpsie i");
    const int tmplen = (length + 3) >> 2;
    vTX_FIFO_INF.val = read_mmaped_reg(base, TX_FIFO_INF);
    kprint("transmit data used: %d\n", vTX_FIFO_INF.feild.TXD_FREE);

    for(int i=0; i<tmplen; i++, data++)
    {
        write_mmaped_reg(base, TX_DATA_FIFO, *data);
    }


    do {
        vTX_FIFO_INF.val = read_mmaped_reg(base, TX_FIFO_INF);
    } while (0 ==  vTX_FIFO_INF.feild.TXS_USED);

    kprint("transmit status used: %d\n", vTX_FIFO_INF.feild.TXS_USED);
    kprint("transmit data used: %d\n", vTX_FIFO_INF.feild.TXD_FREE);

    vTX_STATUS.val = read_mmaped_reg(base, TX_STATUS_FIFO);
    kprint("transmit status: %x\n", vTX_STATUS.val);

    vTX_FIFO_INF.val = read_mmaped_reg(base, TX_FIFO_INF);
    kprint("transmit status used: %d\n", vTX_FIFO_INF.feild.TXS_USED);

    return 0;
}


static int receive(void* base, void* buffer) {
    LOCAL_U32_FEILD(vRX_FIFO_INF)
    LOCAL_U32_FEILD(vRX_STATUS)
retry:
    vRX_FIFO_INF.val = read_mmaped_reg(base, RX_FIFO_INF);
    if (0 ==  vRX_FIFO_INF.feild.RXS_USED) {
        goto retry;
    }

    kprint("rx status used: %d\n", vRX_FIFO_INF.feild.RXS_USED);
    kprint("rx datafifo used: %d\n", vRX_FIFO_INF.feild.RXD_USED);

    vRX_STATUS.val = read_mmaped_reg(base, RX_STATUS_FIFO);
    kprint("rx status : %x\n", vRX_STATUS.val);
    kprint("packet leng: %d\n",vRX_STATUS.feild.packet_len);
    kprint("error reason: %x\n",vRX_STATUS.feild.error_reason);
    kprint("filter fail: %x\n",vRX_STATUS.feild.filter_fail);

    // write_mmaped_reg(base, RX_CFG, 0);

    
    if(vRX_STATUS.feild.error) {
        return -1;
    }

    const int pktlen = vRX_STATUS.feild.packet_len;
    const int tmplen = (pktlen + 3) / 4;

    uint32 *data = (uint32 *)buffer;
    for(int i=0; i< tmplen; i++, data++)
    {
        uint32 value = read_mmaped_reg(base, RX_DATA_FIFO);
        *data = value;
        kprint("[%3d] %x\n", i, value);
    }


    vRX_FIFO_INF.val = read_mmaped_reg(base, RX_FIFO_INF);
    kprint("rx status used: %d\n", vRX_FIFO_INF.feild.RXS_USED);
    kprint("rx datafifo used: %d\n", vRX_FIFO_INF.feild.RXD_USED);
    return 0;
}

static int init(AstralDevice dev, object arg)
{
    address base = dev->vbase;

    LOCAL_U32_FEILD(vID_REV)
    LOCAL_U32_FEILD(vMAC_CR)
    LOCAL_U32_FEILD(vPMT_CTRL)
    LOCAL_U32_FEILD(vPHY_ID1)
    LOCAL_U32_FEILD(vPHY_ID2)
    LOCAL_U32_FEILD(vHW_CFG)
    LOCAL_U32_FEILD(vPHY_CR)
    LOCAL_U32_FEILD(vRX_CFG)
    LOCAL_U32_FEILD(vTX_CFG)
    LOCAL_U32_FEILD(vIRQ_CFG)
    LOCAL_U32_FEILD(vINT_EN)

    // read mac chipid and revision

    vID_REV.val = read_mmaped_reg(base, ID_REV);
    kprint("lan911x chip-id  : %x\n", vID_REV.feild.chipid);
    kprint("lan911x revision : %x\n", vID_REV.feild.revision);

    vINT_EN.feild.PHY_INT = 1;
    vINT_EN.feild.RXD_INT = 1;
    vINT_EN.feild.RXE = 1;
    vIRQ_CFG.feild.IRQ_EN = 1;
    write_mmaped_reg(base, INT_EN, vINT_EN.val);
    write_mmaped_reg(base, IRQ_CFG, vIRQ_CFG.val);
    

    // set power controller
    vPMT_CTRL.val = read_mmaped_reg(base, PMT_CTRL);
    // vPMT_CTRL.feild.PHY_RST = 1;
    write_mmaped_reg(base, PMT_CTRL, vPMT_CTRL.val);

    kprint("lan911x status: %x\n", vPMT_CTRL.feild.ready);

    vMAC_CR.feild.TXEN = 1;
    vMAC_CR.feild.RXEN = 1;
    vMAC_CR.feild.LOOPBK = 1;
    vMAC_CR.feild.RXALL = 1;
    write_mac_reg(base, MAC_CR, vMAC_CR.val);

    vRX_CFG.feild.RXDOFF = ALIGN_4B;
    write_mmaped_reg(base, RX_CFG, vRX_CFG.val);

    // read phy revision and chip id
    // inner phy is addressed as 0b00001
    vPHY_ID1.val = read_phy_reg(base, 0b00001, PHY_ID1);
    kprint("phy id num:  %x\n", vPHY_ID1.val);
    vPHY_ID2.val = read_phy_reg(base, 0b00001, PHY_ID2);
    kprint("phy version: %x\n", vPHY_ID2.val);

    write_phy_reg(base, 0b00001, ANA, 0x01e1);

    vHW_CFG.feild.MBO = 1;
    vHW_CFG.feild.Mode32n16 = 1;
    vHW_CFG.feild.TX_FIF_SZ = 6; // 6KB
    write_mmaped_reg(base, HW_CFG, vHW_CFG.val);

    char mac[7]={0x1a,0x2b,0x3c,0x4d,0x5e,0x6f};
    set_mac_address(base, mac);

    vPHY_CR.feild.loopback = 1;
    vPHY_CR.feild.speed = 1; // 100Mbps
    write_phy_reg(base, 0b00001, PHY_CR, vPHY_CR.val);

    vTX_CFG.feild.TX_ON = 1;
    vTX_CFG.feild.TXSAO = 0;
    write_mmaped_reg(base, TX_CFG, vTX_CFG.val);

    for(int i=0; i<100000000; i++){}


    char buf[120];
    for(int i=0; i<120; i++)buf[i]=i;
    buf[0]=0x1a;
    buf[1]=0x2b;
    buf[2]=0x3c;
    buf[3]=0x4d;
    buf[4]=0x5e;
    buf[5]=0x6f;
    transmit(base, buf, 100);

    for(int i=0; i<1000000; i++){}

    char bufr[120];
    for(int i=0; i<120; i++)bufr[i]=0;

    receive(base, bufr);
    for(int i=0; i<120; i++)
    {
        kprint("%3d:  %x %d\n", i, bufr[i], bufr[i]);
    }
    return 0;
}

static struct AstralDriver _driver = {
    .dwrite = NULL,
    .dread = NULL,
    .dopen = init,
    .dclose = NULL,
    .description = "lan9118 driver"};

static void lan911x_register(AstralDevice arg)
{
    boolean status = register_driver(MIDv4(96, 1, 0, 5), &_driver);
    kprint("register for %s done, status %d\n",
           _driver.description,
           status);
}

STATIC_REGISTER_DRIVER(lan911x_register)
